package com.carrot.gateway.entity;

import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.carrot.common.utils.AESUtils;
import com.carrot.gateway.filter.handler.auth.AuthHandler;
import com.carrot.gateway.util.AuthUtil;
import com.carrot.gateway.util.GetPostBodyUtil;
import com.carrot.gateway.util.IpAddressUtil;
import com.carrot.gateway.util.RedisUtils;
import lombok.Data;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.MultiValueMap;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Objects;

/**
 * 访问日志类
 * @author 刘红真
 */
@Data
public class AccessLog implements Serializable {

    private RedisTemplate<String, Object> redisTemplate;

    private static final HashSet<String> SECURITY_API=new HashSet<>();

    static {
        SECURITY_API.add("/api/auth/v1/token/refresh");
        SECURITY_API.add("/api/auth/v1/sms-captcha");
        SECURITY_API.add("/api/auth/v1/revocationToken");
    }

    /**
     * 请求ID 使用traceId
     */
    private String traceId;
    /**
     * 请求时间戳
     */
    private Long requestTimestamp;
    /**
     * 请求方法
     */
    private String requestMethod;
    /**
     * 请求URL
     */
    private String requestUrl;
    /**
     * 请求参数
     */
    private String requestParam;
    /**
     * 请求签名
     */
    private String requestSign;
    /**
     * 请求IP
     */
    private String requestIp;
    /**
     * 用户代理
     */
    private String userAgent;
    /**
     * 响应http状态码
     */
    private Integer responseHttpCode;
    /**
     * 响应业务状态码
     */
    private String responseBusinessCode;
    /**
     * 响应时间
     */
    private Long responseTime;
    /**
     * 响应参数
     */
    private String responseParam;
    /**
     * 请求来源
     */
    private String referer;
    /**
     * 用户id
     */
    private String userId;
    /**
     * 用户名称
     */
    private String userName;


    public void buildRequestLog(ServerHttpRequest request){
        //请求ID
        this.traceId = UUID.randomUUID().toString(true);;
        //请求时间戳
        this.requestTimestamp = System.currentTimeMillis();
        //请求方法
        this.requestMethod = Objects.requireNonNull(request.getMethod()).name();
        //请求URL
        this.requestUrl = request.getPath().toString();
        //请求签名
        this.requestSign = request.getHeaders().getFirst(AuthHandler.SIGN_HEADER);
        //请求IP
        this.requestIp= IpAddressUtil.getClientIpAddress(request);
        //用户代理
        this.userAgent= request.getHeaders().getFirst(HttpHeaders.USER_AGENT);
        //请求来源
        this.referer= request.getHeaders().getFirst(HttpHeaders.REFERER);
        //请求参数
        if(HttpMethod.POST.name().equals(this.requestMethod)||HttpMethod.PUT.name().equals(this.requestMethod)
                ||HttpMethod.PATCH.name().equals(this.requestMethod)){
            String body = GetPostBodyUtil.resolveBodyFromRequest(request);
            if(body!=null){
                String newBody = body.replaceAll("\\s", "");
                buildRequestParam(newBody);
            }
        }else if(HttpMethod.GET.name().equals(this.requestMethod)){
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            String queryParamsStr = queryParams.toString();
            buildRequestParam(queryParamsStr);
        }
        //请求用户
        HttpHeaders headers = request.getHeaders();
        String token = AuthUtil.handleToken(headers, HttpHeaders.AUTHORIZATION);
        if("/uaa/api/oauth2/token".equals(this.requestUrl)){
            this.userId="03";
            this.userName="客户端认证";
        } else if("/api/auth/v1/login".equals(this.requestUrl)){
            //this.userId="04";
            this.userName="登录";
        }else if(redisTemplate==null||token==null|| token.isEmpty()){
            this.userId="01";
            this.userName="未知用户";
        }else {
            String accessTokenKey= RedisUtils.getUserOnLineAccessTokenKey(token);
            Boolean exist = redisTemplate.hasKey(accessTokenKey);
            if(Boolean.TRUE.equals(exist)){
                JSONObject userInfo = JSONUtil.parseObj(redisTemplate.opsForValue().get(accessTokenKey));
                this.userId=userInfo.getStr("uid")==null?userInfo.getStr("clientId"):userInfo.getStr("uid");
                this.userName=userInfo.getStr("nickname");
            }else {
                this.userId="02";
                this.userName="无效令牌";
            }
        }
    }

    public void buildRequestParam(String param){
        try {
            this.requestParam=SECURITY_API.contains(this.requestUrl) ? AESUtils.encrypt(param):param;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void buildResponseTime(){
        //响应时间
        Long responseTime=System.currentTimeMillis();
        this.responseTime=responseTime-this.requestTimestamp;
    }

    public void buildResponseBody(HttpStatus httpStatus, String responseData){
        if(httpStatus.equals(HttpStatus.OK)&&responseData!=null){
            JSONObject jsonObject = JSONUtil.parseObj(responseData);
            String code=jsonObject.getStr("code");
            if(!"200".equals(code)){
                this.responseParam=jsonObject.getStr("msg");
                this.responseBusinessCode=code;
            }
        }
        if(!httpStatus.equals(HttpStatus.OK)){
            this.responseParam=responseData;
        }
    }

    public AccessLog(RedisTemplate<String, Object> redisTemplate){
        this.redisTemplate=redisTemplate;
    }

    public AccessLog(){
    }

    @Override
    public String toString() {
        return traceId +
                "|" + requestTimestamp +
                "|" + requestMethod +
                "|" + requestUrl +
                "|" + requestParam +
                "|" + requestSign +
                "|" + requestIp +
                "|" + userAgent +
                "|" + responseHttpCode +
                "|" + responseBusinessCode +
                "|" + responseTime +
                "|" + responseParam +
                "|" + referer +
                "|" + userId+
                "|" + userName;
    }

}
